package com.whmall.service.crm.module.supplierreview;

import com.whmall.component.entity.dto.PageReqDTO;
import com.whmall.component.entity.dto.PageRspDTO;
import com.whmall.component.enumeration.OperatorTypeEnum;
import com.whmall.component.util.FlatListUtils;
import com.whmall.service.crm.entity.dto.supplierreview.*;
import com.whmall.service.crm.module.supplierreview.entity.*;
import com.whmall.service.crm.module.supplierreview.entity.qo.SupplierReviewTagStatisticsQO;
import com.whmall.service.crm.module.supplierreview.impl.dao.SupplierReviewTagStatisticsMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * View Builder for SupplierReview
 *
 * @author alex
 * @since 2021/1/18
 */
@Component
@RequiredArgsConstructor
public class SupplierReviewViewBuilder {
    private final SupplierReviewRepository supplierReviewRepository;
    private final SupplierReviewTagCategoryManager supplierReviewTagCategoryManager;
    private final SupplierReviewTagManager supplierReviewTagManager;
    private final SupplierReviewTagStatisticsMapper supplierReviewTagStatisticsMapper;

    public List<SupplierReviewTagCategoryRspDTO> buildAllTagCategory(boolean includeTags) {
        Map<Integer, List<SupplierReviewTagRspDTO>> tagMap = !includeTags
                ? Collections.emptyMap()
                : buildAllTag()
                .stream()
                .collect(Collectors.groupingBy(SupplierReviewTagRspDTO::getCategory));

        return supplierReviewTagCategoryManager.findAll()
                .stream()
                .map(e -> {
                    SupplierReviewTagCategoryRspDTO dto = new SupplierReviewTagCategoryRspDTO();
                    dto.setId(e.getId());
                    dto.setName(e.getName());
                    dto.setSequence(e.getSequence());
                    dto.setIsEnabled(Objects.equals(e.getIsEnabled(), 1));
                    dto.setTags(includeTags
                            ? Optional.ofNullable(tagMap.get(e.getId())).orElse(Collections.emptyList())
                            : null);
                    return dto;
                })
                .collect(Collectors.toList());
    }

    public List<SupplierReviewTagRspDTO> buildAllTag() {
        return supplierReviewTagManager.findAll()
                .stream()
                .map(this::tagVo2DTO)
                .collect(Collectors.toList());
    }

    /**
     * 分页查询供应商评论
     *
     * @param pageRequest 分页请求
     * @param criteria    查询条件
     * @return 分页的供应商评论列表
     */
    public PageRspDTO<SupplierReviewRspDTO> buildReviewPage(PageReqDTO pageRequest,
                                                            SupplierReviewQueryCriteria criteria) {
        PageRspDTO<SupplierReviewDO> page =
                supplierReviewRepository.findReviewPageByQueryCriteria(pageRequest, criteria);
        if (page.getRecords().isEmpty()) {
            return page.map(e -> null);
        }

        List<Long> reviewIds = page.getRecords()
                .stream()
                .map(SupplierReviewDO::getId)
                .collect(Collectors.toList());

        Map<Long, SupplierReviewInformationDO> informationMap = criteria.isIncludeContent()
                ? supplierReviewRepository.findInformationByReviewIds(reviewIds)
                .stream()
                .collect(Collectors.toMap(SupplierReviewInformationDO::getId, e -> e))
                : null;

        Map<Long, List<SupplierReviewAttachmentDO>> attachmentsMap = criteria.isIncludeAttachments()
                ? supplierReviewRepository.findAttachementByReviewIds(reviewIds)
                .stream()
                .collect(Collectors.groupingBy(SupplierReviewAttachmentDO::getReviewId))
                : null;

        Map<Integer, SupplierReviewTagRspDTO> tagMap = criteria.isIncludeTags()
                ? buildTagByIds(page.getRecords()
                .stream()
                .flatMap(e -> FlatListUtils.toList(e.getTagList(), Integer::valueOf).stream())
                .distinct()
                .collect(Collectors.toList()))
                .stream()
                .collect(Collectors.toMap(SupplierReviewTagRspDTO::getId, e -> e))
                : null;

        return page.map(e -> {
            SupplierReviewRspDTO dto = new SupplierReviewRspDTO();

            dto.setId(e.getId());
            dto.setIsDeleted(e.getIsDeleted());
            dto.setIsCounted(e.getIsCounted());
            dto.setCompanyId(e.getCompanyId());
            dto.setPageSource(SupplierReviewPageSourceEnum.fromNullableValue(e.getPageSource()));
            dto.setBusinessType(SupplierReviewBusinessTypeEnum.fromNullableValue(e.getBusinessType()));
            dto.setBusinessId(e.getBusinessId());
            dto.setBusinessNo(e.getBusinessNo());
            dto.setTagIds(FlatListUtils.toList(e.getTagList(), Integer::valueOf));
            dto.setCreateType(OperatorTypeEnum.fromValue(e.getCreateType()));
            dto.setCreateId(e.getCreateId());
            dto.setCreateTime(e.getCreateTime());
            dto.setUpdateType(OperatorTypeEnum.fromValue(e.getUpdateType()));
            dto.setUpdateId(e.getUpdateId());
            dto.setUpdateTime(e.getUpdateTime());

            if (criteria.isIncludeContent()) {
                assert informationMap != null;
                dto.setContent(informationMap.get(e.getId()).getContent());
            }

            if (criteria.isIncludeAttachments()) {
                assert attachmentsMap != null;
                dto.setAttachmentUrls(Optional.ofNullable(attachmentsMap.get(e.getId()))
                        .map(c -> c.stream()
                                .map(SupplierReviewAttachmentDO::getAttachmentUrl)
                                .collect(Collectors.toList()))
                        .orElse(Collections.emptyList()));
            }

            if (criteria.isIncludeTags()) {
                assert tagMap != null;
                dto.setTags(dto.getTagIds()
                        .stream()
                        .map(tagMap::get)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList()));
            }

            return dto;
        });
    }

    /**
     * 根据公司ID查找该公司的所有评论标签统计列表
     *
     * @param companyId      公司ID，即CRM供应商公司ID
     * @param includeTagInfo 返回数据内是否包含标签信息数据
     * @return 供应商评论标签统计
     */
    public List<SupplierReviewTagStatisticsRspDTO> buildReviewTagStats(Long companyId,
                                                                       boolean includeTagInfo) {
        List<SupplierReviewTagStatisticsRspDTO> results = supplierReviewRepository.findReviewTagStatsByCompanyId(companyId)
                .stream()
                .map(e -> {
                    SupplierReviewTagStatisticsRspDTO dto = new SupplierReviewTagStatisticsRspDTO();
                    dto.setId(e.getId());
                    dto.setCompanyId(e.getCompanyId());
                    dto.setTagId(e.getTag());
                    dto.setTagCount(e.getTagCount1());
                    dto.setCreateTime(e.getCreateTime());
                    dto.setUpdateTime(e.getUpdateTime());
                    return dto;
                })
                .collect(Collectors.toList());

        if (includeTagInfo) {
            List<Integer> tagIds = results.stream()
                    .map(SupplierReviewTagStatisticsRspDTO::getTagId)
                    .distinct()
                    .collect(Collectors.toList());
            Map<Integer, SupplierReviewTagVO> tagMap = supplierReviewTagManager.findByIds(tagIds)
                    .stream()
                    .collect(Collectors.toMap(SupplierReviewTagVO::getId, e -> e));

            results.forEach(e -> e.setTagInfo(Optional.of(tagMap.get(e.getTagId()))
                    .map(this::tagVo2DTO)
                    .orElse(null)));
        }

        return results;
    }

    /**
     * 根据公司ID集合查询评论统计
     * @param companyIds
     * @return
     */
    public Map<Long, List<SupplierReviewTagStatisticsRspDTO>> findReviewTagStatisticsByCompanyIds(List<Long> companyIds){
        if(CollectionUtils.isEmpty(companyIds)){
            return Collections.emptyMap();
        }
        List<SupplierReviewTagStatisticsDO> statistics = supplierReviewTagStatisticsMapper.findBy(
                SupplierReviewTagStatisticsQO.builder().companyIds(companyIds).build());
        Map<Integer, SupplierReviewTagRspDTO> tags = buildTagByIds(
                statistics.stream().map(e -> e.getTag()).distinct().collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(e -> e.getId(), e -> e));
        return statistics.stream().map(e -> {
            SupplierReviewTagStatisticsRspDTO rspDTO = new SupplierReviewTagStatisticsRspDTO();
            BeanUtils.copyProperties(e, rspDTO);
            rspDTO.setIsGood(tags.get(e.getTag()).getIsGood());
            return rspDTO;
        }).collect(Collectors.groupingBy(e -> e.getCompanyId(), Collectors.toList()));
    }

    private List<SupplierReviewTagRspDTO> buildTagByIds(List<Integer> tagIds) {
        return supplierReviewTagManager.findByIds(tagIds)
                .stream()
                .map(this::tagVo2DTO)
                .collect(Collectors.toList());
    }

    private SupplierReviewTagRspDTO tagVo2DTO(SupplierReviewTagVO e) {
        SupplierReviewTagRspDTO dto = new SupplierReviewTagRspDTO();
        dto.setId(e.getId());
        dto.setName(e.getName());
        dto.setCategory(e.getCategory());
        dto.setIsGood(Objects.equals(e.getIsGood(), 1));
        dto.setIsEnabled(Objects.equals(e.getIsEnabled(), 1));
        dto.setSequence(e.getSequence());
        return dto;
    }
}
